package com.guonl.event.consume;

import com.alibaba.fastjson.JSON;
import com.guonl.event.model.SyncSqlEvent;
import com.guonl.plus.entity.IndexTUserPlus;
import com.guonl.plus.mapper.DataSqlExecuteMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.event.TransactionPhase;
import org.springframework.transaction.event.TransactionalEventListener;
import org.apache.commons.beanutils.BeanUtils;

import javax.annotation.PostConstruct;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 可以自由匹配宽表字段
 */
@Slf4j
@Component
public class SyncSqlEventConsumer {

    private static final char UNDER_LINE = '_';

    private static final String TARGET_TABLE = "index_t_user";

    private static List<String> TARGET_FIELDS = new ArrayList<>();

    @PostConstruct
    public void init() throws Exception {
        TARGET_FIELDS = getTragetDBColumns(IndexTUserPlus.class);
    }


    @Autowired
    private DataSqlExecuteMapper dataSqlExecuteMapper;

    @TransactionalEventListener(
            fallbackExecution = true,
            phase = TransactionPhase.AFTER_COMPLETION,
            classes = SyncSqlEvent.class)
    public void doAsyncSql(SyncSqlEvent sqlEvent) {
        log.debug("宽表数据同步--update sql->>接收消息:{}", JSON.toJSONString(sqlEvent));
        String tableName = sqlEvent.getTableName();
        String sql = sqlEvent.getExecuteSql();
//        sql.replace(tableName, "index_transport_note");

        //不包含where条件的sql不执行
        if (!sql.contains("WHERE")) {
            return;
        }

        int setIndex = sql.indexOf("SET");
        int whereIndex = sql.indexOf("WHERE");

        String setSubSql = sql.substring(setIndex + 3, whereIndex);
        String whereSubSql = sql.substring(whereIndex, sql.length());

        Map<String, String> fieldValueMap = new HashMap<>();
//        String regex = "\\s+([^=,]+)\\s*=\\s*([^,]+)";//会丢失部分字段
        String regex = "\\s*([^=,]+)\\s*=\\s*([^,]+)\\s*(?:,|$)";
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(setSubSql);

        while (matcher.find()) {
            String column = matcher.group(1).trim();
            String value = matcher.group(2).trim();
            fieldValueMap.put(column, value);
        }

        StringBuilder builderSql = new StringBuilder("UPDATE " + TARGET_TABLE + " SET");

        AtomicInteger columnCount = new AtomicInteger(0);
        fieldValueMap.forEach((k, v) -> {
            if (TARGET_FIELDS.contains(k)) {
                builderSql.append(" " + k + "=" + v + ",");
                columnCount.getAndIncrement();
            }
        });

        if (columnCount.get() == 0) {
            return;
        }

        String newUpdateSql = builderSql.deleteCharAt(builderSql.length() - 1).append(" " + whereSubSql).toString();
        try {
            dataSqlExecuteMapper.executeNativeUpdateSql(newUpdateSql);
            log.info("宽表数据同步--成功,sql:{}", newUpdateSql);
        } catch (Exception e) {
            log.error("宽表数据同步--失败,sql:{}", newUpdateSql, e);
        }
    }

    public List<String> getTragetDBColumns(Class clz) throws Exception {
        List<String> columns = new ArrayList<>();
        Object instance = clz.newInstance();
        Map<String, String> describe = BeanUtils.describe(instance);
        describe.forEach((k, v) -> {
            columns.add(toUnderlineCase(k));
        });
        return columns;
    }

    //驼峰转下划线
    public String toUnderlineCase(String name) {
        if (name == null) {
            return null;
        }
        int len = name.length();
        StringBuilder res = new StringBuilder(len + 2);
        char pre = 0;
        for (int i = 0; i < len; i++) {
            char ch = name.charAt(i);
            if (Character.isUpperCase(ch)) {
                if (pre != UNDER_LINE) {
                    res.append(UNDER_LINE);
                }
                res.append(Character.toLowerCase(ch));
            } else {
                res.append(ch);
            }
            pre = ch;
        }
        return res.toString();

    }

}
